/**
 * Copyright 2011 Adrian Witas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.abstractmeta.commandify.generator;

import org.abstractmeta.code.g.code.JavaMethod;
import org.abstractmeta.code.g.code.JavaModifier;
import org.abstractmeta.code.g.code.JavaType;
import org.abstractmeta.code.g.code.JavaTypeBuilder;
import org.abstractmeta.code.g.core.annotation.SuppressWarningsBuilder;
import org.abstractmeta.code.g.core.code.builder.JavaFieldBuilder;
import org.abstractmeta.code.g.core.code.builder.JavaMethodBuilder;
import org.abstractmeta.code.g.core.extractor.SuperSetterFieldExtractor;
import org.abstractmeta.code.g.core.generator.ClassGenerator;
import org.abstractmeta.code.g.core.internal.ParameterizedTypeImpl;
import org.abstractmeta.code.g.core.internal.TypeNameWrapper;
import org.abstractmeta.code.g.core.internal.TypeVariableImpl;
import org.abstractmeta.code.g.core.util.JavaTypeUtil;
import org.abstractmeta.code.g.extractor.MethodExtractor;
import org.abstractmeta.code.g.generator.Context;
import org.abstractmeta.commandify.Commandify;
import org.abstractmeta.commandify.OwnerSelfCommandifyAccessor;
import org.abstractmeta.commandify.command.Commands;
import org.abstractmeta.commandify.core.CommandsImpl;
import org.abstractmeta.commandify.core.command.OwnerSelfCommandifyAccessorImpl;
import org.abstractmeta.commandify.generator.builder.CommandifyClassBuilder;
import org.abstractmeta.reflectify.Accessor;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;

/**
 * Commandify generator
 *
 * @author Adrian Witas
 */
public class CommandifyGenerator extends ClassGenerator {


    protected JavaTypeBuilder getClassBuilder(String targetName, JavaType sourceJavaType, Context context) {
        JavaTypeBuilder result = new CommandifyClassBuilder(targetName, sourceJavaType, context);
        result.addAnnotations(new SuppressWarningsBuilder().addValue("unchecked").build());
        Type sourceType = new TypeNameWrapper(sourceJavaType.getName());
        result.addSuperInterfaces(new ParameterizedTypeImpl(null, Commandify.class, sourceType));
        result.addGenericTypeVariable("T", sourceType);
        Type fieldImplementationType = new ParameterizedTypeImpl(null, CommandsImpl.class, sourceType);
        result.getImporter().addTypes(fieldImplementationType);
        String fieldImplementationFragment = result.getImporter().getSimpleTypeName(fieldImplementationType);
        String arguments = "";
        String simpleClassName = result.getImporter().getSimpleTypeName(sourceType);
        result.addField(new JavaFieldBuilder()
                .setName("commands")
                .setType(new ParameterizedTypeImpl(null, Commands.class, sourceType))
                .addModifiers(JavaModifier.PRIVATE, JavaModifier.TRANSIENT).setImmutable(true)

                .setConstructorInitValue(String.format("new %s(%s.class%s)", fieldImplementationFragment, simpleClassName, arguments))
                .build()
        );
        result.addField(new JavaFieldBuilder()
                .setName("commandifyType")
                .setType(new ParameterizedTypeImpl(null, Class.class, sourceType))
                .addModifiers(JavaModifier.PROTECTED, JavaModifier.TRANSIENT).setImmutable(true)
                .setConstructorInitValue(String.format("%s.class", simpleClassName))
                .build()
        );


        JavaMethod commandifyAccessorMethod = buildCommandifyAccessorMethod(result, sourceJavaType);
        result.addMethod(commandifyAccessorMethod);
        return result;
    }

    private JavaMethod buildCommandifyAccessorMethod(JavaTypeBuilder owner, JavaType sourceJavaType) {
        JavaMethodBuilder result = new JavaMethodBuilder().setResultType(new ParameterizedTypeImpl(null, OwnerSelfCommandifyAccessor.class, new TypeVariableImpl("T", null), Object.class));
        result.addAnnotation(new SuppressWarningsBuilder().addValue("unchecked").build());
        result.addModifiers(JavaModifier.PUBLIC).setName("getCommandifyAccessor");
        result.addParameter("source", Object.class);


        result.addBodyLines("Accessor accessor = commands.getAccessor(source);");
        result.addBodyLines(String.format("return new %s(source, this, accessor);", OwnerSelfCommandifyAccessorImpl.class.getSimpleName()));
        owner.getImporter().addTypes(Accessor.class);
        owner.getImporter().addTypes(OwnerSelfCommandifyAccessorImpl.class);
        return result.build();
    }

    @Override
    protected Collection<MethodExtractor> getMethodExtractors() {
        Collection<MethodExtractor> result = new LinkedHashSet<MethodExtractor>();
        result.add(new SuperSetterFieldExtractor());
        result.addAll(super.getMethodExtractors());
        return result;
    }


}
